// -----------------------------------------------------------------------------
// sys_autoscale_h
// -----------------------------------------------------------------------------
/*
    Creature scaling and levelup system

*/
// -----------------------------------------------------------------------------
// owner: georg zoeller
// -----------------------------------------------------------------------------

#include "ascl_sys_chargen_h"
#include "ai_main_h_2"
#include "plt_gen00pt_backgrounds"
#include "wrappers_h"
const float AS_CREATURE_BASE_HEALTH  = 50.0f;
const float AS_CREATURE_BASE_STAMINA = 50.0f;

const string AUTO_SCALE_FACTOR_HEALTH = "AsHPScale";
const string AUTO_SCALE_FACTOR_ATTR1 ="Attr1";
const string AUTO_SCALE_FACTOR_ATTR2 ="Attr2";
const string AUTO_SCALE_FACTOR_ATTR3 ="Attr3";

const float AS_POINTS_PER_LEVEL = 3.0f;

const int AS_STRATEGY_DEFAULT = 0;
const int AS_STRATEGY_RANGED  = 3;

const int AS_MAX_PACKAGE_ABILITIES = 12;

int AS_GetCreatureLevelToScale(object oCreature, int nAreaLevel)
{
    int nLevelToScale = nAreaLevel;
    int nRank = GetCreatureRank(oCreature);
    int nApr = GetAppearanceType(oCreature);
    int nMinLevel = GetLocalInt(oCreature, MIN_LEVEL);
    int nDisableAppearanceLevelLimit = GetLocalInt(GetModule(), DISABLE_APPEARANCE_LEVEL_LIMITS); // global module switch
    object oArea = GetArea(oCreature);
    int nAreaID = GetLocalInt(oArea, AREA_ID);
    int nDisableAppearanceLevelLimitPerArea = GetM2DAInt(225, "IgnoreAppMaxLevel", nAreaID);

    // scale by rank
    int nLevelRankScale = GetM2DAInt(Diff_GetAutoScaleTable(), "nLevelScale", nRank);
    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_GetCreatureLevelToScale","rank level scale: " + IntToString(nLevelRankScale));
    #endif

    nLevelToScale = Max(1,nLevelToScale + nLevelRankScale);
    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_GetCreatureLevelToScale","level to scale: " + IntToString(nLevelToScale));
    #endif

    // limit max level per appearance
    int nMaxScaleLevel = GetM2DAInt(TABLE_APPEARANCE, "MaxScaleLevel", nApr);

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_GetCreatureLevelToScale","max scale level: " + IntToString(nMaxScaleLevel));
    #endif

    if (!IsSummoned(oCreature) && !nDisableAppearanceLevelLimit && !nDisableAppearanceLevelLimitPerArea)
    {
        if(nRank == CREATURE_RANK_CRITTER || (nRank == CREATURE_RANK_NORMAL) || nRank == CREATURE_RANK_ONE_HIT_KILL || nRank == CREATURE_RANK_WEAK_NORMAL)
        {
            if(nMaxScaleLevel > 0 && nLevelToScale > nMaxScaleLevel)
                nLevelToScale = nMaxScaleLevel;
        }
    }

    if(nLevelToScale < nMinLevel)
        nLevelToScale = nMinLevel;

    // Must be last
    int nClimaxArmy = GetLocalInt(oCreature, CLIMAX_ARMY_ID);
    if(nClimaxArmy > 0 && GetTag(OBJECT_SELF) != "cli000cr_army_legion")
    {
        int nClimaxMaxLevel = GetM2DAInt(TABLE_PLOTACTIONS, "MaxLevel", nClimaxArmy);
        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_GetCreatureLevelToScale","critter creature belong to climax army: " + IntToString(nClimaxArmy) +
            ", max level: " + IntToString(nClimaxMaxLevel));
        #endif
        if(nClimaxMaxLevel > 0 && nLevelToScale > nClimaxMaxLevel)
            nLevelToScale = nClimaxMaxLevel;
    }

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_GetCreatureLevelToScale","area level: " + IntToString(nAreaLevel) + ", final level: " + IntToString(nLevelToScale));
    #endif

    if(nRank == CREATURE_RANK_ONE_HIT_KILL && nLevelToScale > 10)
        nLevelToScale = 10;

    return nLevelToScale;
}

int As_AddAbility(object oCreature, int nPackage, string sAbilityIndex, int nNum)
{
    int nAbility = 0;
    nAbility = GetM2DAInt(TABLE_PACKAGES, sAbilityIndex, nPackage);
    if(nAbility <= 0) // -1 used to skip
        return nAbility;
    AddAbility(oCreature, nAbility);
    if(GetCreatureRank(oCreature) == CREATURE_RANK_PLAYER)
        SetQuickslot(oCreature, -1, nAbility);
    nNum++;

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_AssignAbilities","Ability Added: " + Log_GetAbilityNameById(nAbility));
    #endif

    return nNum;
}

void AS_AssignAbilities(object oCreature, int nAbilitiesToGive)
{
    int nPackage = GetPackage(oCreature);
    int nNum = 0;
    int bIsFollower = FALSE;
    if(GetCreatureRank(oCreature) == CREATURE_RANK_PLAYER)
        bIsFollower = TRUE;

    if(nAbilitiesToGive < 1) nAbilitiesToGive = 1;
    else if(nAbilitiesToGive > AS_MAX_PACKAGE_ABILITIES) nAbilitiesToGive = AS_MAX_PACKAGE_ABILITIES;

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "AS_AssignAbilities","Number of abilities to assign: " + IntToString(nAbilitiesToGive));
    #endif

    nNum = As_AddAbility(oCreature, nPackage, "Ability1", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability2", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability3", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability4", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability5", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability6", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability7", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability8", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability9", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability10", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability11", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

    nNum = As_AddAbility(oCreature, nPackage, "Ability12", nNum);
    if(nNum == 0 || nNum == nAbilitiesToGive) return;

}

int  AS_SelectStrategy(object oCreature, int nClass, int bIgnorePackage=0)
{

    int nStrategy = AS_STRATEGY_DEFAULT;

    int nPackage = (bIgnorePackage== 0)?GetPackage(oCreature) : 0;

    if (nPackage > 0)
    {
        nStrategy = GetM2DAInt(TABLE_PACKAGES,"Strategy",nPackage);

        #ifdef DEBUG
        if (nStrategy > 0)
            Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Determine strategy from PACKAGE ... success");
        #endif
    }

    if(nStrategy == AS_STRATEGY_DEFAULT) // no strategy from package
    {
        nStrategy+=nClass*100;              // Class package ids are class id * 100
    }

    #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","using strategy: " + GetM2DAString(Diff_GetAutoScaleTable(),"Label",nStrategy));
    #endif

    return (nStrategy);
}


void AS_SetDepletable(object oCreature, float fMax, int nProp)
{
    if (fMax ==  0.0f)
    {
        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE,"sys_autoscale_h.SetBaseHealth","Data error: Health set to 0");
        #endif

        fMax = 1.0f;
    }
    SetCreatureProperty(oCreature,nProp,  fMax , PROPERTY_VALUE_BASE );
    float fHeal = GetCreatureProperty(oCreature, nProp);
    SetCreatureProperty(oCreature,nProp,  fHeal, PROPERTY_VALUE_CURRENT ) ;
}



int AS_DetermineClass(object oCreature)
{
    if (GetCreatureCurrentClass(oCreature) == 0)
    {

        int nPackage = GetPackage(oCreature);

        if (nPackage > 0)
        {
            int nClass = GetM2DAInt(TABLE_PACKAGES,"StartingClass",nPackage);

            if (nClass > 0)
            {
                #ifdef DEBUG
                Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Determine class from PACKAGE ... success");
                #endif
                return nClass;
            }
        }

        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Determined class from PACKAGE ... failed, reverting to legacy code");
        #endif

        // LEGACY CODE BELOW HERE, fallback for badly setup creatures.

        object oItem = GetItemInEquipSlot(INVENTORY_SLOT_CHEST, oCreature);
        int nBaseItem = GetBaseItemType(oItem);
        int nClass;
        if (nBaseItem == 38 /*clothing*/)
        {
            // ---------------------------------------------------------------------
            // Critters can't be mages
            // ---------------------------------------------------------------------
            if (GetCreatureRank(oCreature) != CREATURE_RANK_CRITTER)
            {
                return CLASS_WIZARD;
            }
        }
        return CLASS_WARRIOR;
    }
    else
    {
         return GetCreatureCoreClass(oCreature);
    }
}


void AS_AddClassLevels (object oCreature, int nClass = 0, int nLevels=1, float fRankModifier = 0.0f, int bAdjustXP = TRUE, int bChangedClass = FALSE)
{


    int nSubtractor = 0;
    if (nLevels < 1)
    {
            #ifdef DEBUG
            Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Error adding classlevels. 0 requested...");
            #endif

            return;
    }
    else
    {
        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","AS_AddClassLevels * " + ToString(nLevels));
        #endif
    }


    if (nClass == 0)
    {
        nClass =  AS_DetermineClass(oCreature);
    }

    if (fRankModifier == 0.0f)
    {
        fRankModifier = GetAutoScaleDataFloat(GetCreatureRank(oCreature),AS_RANK_SCALE_FACTOR);
    }

    float fRankModifierHealth = GetAutoScaleDataFloat(GetCreatureRank(oCreature),AS_RANK_HEALTH_SCALE_FACTOR);

    if (fRankModifierHealth == 0.0f)
    {
         fRankModifierHealth = 1.0f;
    }


    int nCurLevel = GetLevel(oCreature);
    // -------------------------------------------------------------------------
    // If the creatures current level if 0/1, we need to apply the initial class
    //
    // Note:
    //   Rank Modifier is applied to only Health and Mana.
    // -------------------------------------------------------------------------
    if (nCurLevel <= 1 && GetCreatureProperty(oCreature,PROPERTY_SIMPLE_EXPERIENCE) < 1.0f)
    {
#ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","New character, picking base class: " + ToString(nClass));
#endif
        Chargen_ApplyClassAttributeModifiers(oCreature,nClass, FALSE);
        Chargen_ApplyClassStatModifiers(oCreature, nClass, FALSE, fRankModifier);


        // ---------------------------------------------------------------------
        // Grant class abilities
        // ---------------------------------------------------------------------
        Chargen_ApplyClassAbilities(oCreature, nClass, FALSE);
        nSubtractor = 1;
        SetCreatureProperty(oCreature, PROPERTY_SIMPLE_LEVEL, 1.0);

    }
    else
    {
        if (bChangedClass)
        {
            Chargen_ApplyClassAbilities(oCreature, nClass, FALSE);
        }
    }


    if (nLevels - nSubtractor >0)
    {

        int nLevelsToAdd = nLevels - nSubtractor;
        // -------------------------------------------------------------------------
        // Set stats that are modified per level.
        // instead of looping, we just multiply by level
        //
        // Note:
        //   Rank Modifier is applied to only Health and Mana.
        // -------------------------------------------------------------------------


        float fDamageMod = GetClassDataFloat(CLASS_DATA_DAMAGE_BONUS    , nClass) * nLevelsToAdd;
        float fHealth    = (GetClassDataFloat(CLASS_DATA_HEALTH_PER_LEVEL, nClass) * fRankModifierHealth) * nLevelsToAdd ;
        float fMana      = GetClassDataFloat(CLASS_DATA_MANA_PER_LEVEL,   nClass) * nLevelsToAdd * fRankModifier;

        Chargen_ModifyCreaturePropertyBase(oCreature,PROPERTY_ATTRIBUTE_DAMAGE_BONUS, fDamageMod);
        Chargen_ModifyCreaturePropertyBase(oCreature,PROPERTY_DEPLETABLE_HEALTH, fHealth);
        Chargen_ModifyCreaturePropertyBase(oCreature,PROPERTY_DEPLETABLE_MANA_STAMINA, fMana);

        // -------------------------------------------------------------------------
        // Increase level to new value
        // -------------------------------------------------------------------------
        Chargen_ModifyCreaturePropertyBase(oCreature, PROPERTY_SIMPLE_LEVEL,IntToFloat(nLevelsToAdd));

        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE,"As_AddClassLevel","Adding " +ToString(nLevelsToAdd) + " levels");
        #endif


    }


    // -------------------------------------------------------------------------
    // Adjust XP to start of new level if requested
    // -------------------------------------------------------------------------
    if (bAdjustXP)
    {
        int nXP =  GetM2DAInt(TABLE_EXPERIENCE,"XP",nLevels);
#ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","adjusting XP to " +ToString(nXP)+ " to the start of level " + ToString(nLevels));
#endif
        Chargen_ModifyCreaturePropertyBase(oCreature, PROPERTY_SIMPLE_EXPERIENCE,IntToFloat(nXP));
    }

    SetCreatureProperty(oCreature, PROPERTY_SIMPLE_CURRENT_CLASS, IntToFloat(nClass));

}


void AS_SetCommonStats(object oCreature)
{
   int nRank               = GetCreatureRank(oCreature);

   // hackety
   float fRegenBonus = 0.0f; //IntToFloat(((nRank > CREATURE_RANK_NORMAL && nRank < CREATURE_RANK_PLAYER)? 2 : 0));

    SetCreatureProperty(oCreature, 51, 1.0);
    SetCreatureProperty(oCreature, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, REGENERATION_STAMINA_COMBAT_DEFAULT + fRegenBonus );

    SetCreatureProperty(oCreature, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, REGENERATION_STAMINA_EXPLORE_DEFAULT);
    SetCreatureProperty(oCreature, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH, REGENERATION_HEALTH_EXPLORE_DEFAULT);

    SetCreatureProperty(oCreature, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH_COMBAT, REGENERATION_HEALTH_COMBAT_DEFAULT + fRegenBonus);

    // Set threat decrease rate
    SetCreatureProperty(oCreature, PROPERTY_SIMPLE_THREAT_DECREASE_RATE, AI_THREAT_DISSOLVE);

}

void AS_CommenceAutoScaling(object oCreature, int nLevel = 1, int nForceClass=0)
{

    int nRank               = GetCreatureRank(oCreature);
    float fTotalPoints      = 3.0;
    int nClass = (nForceClass == 0) ? AS_DetermineClass(oCreature) : nForceClass;

#ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "-------------------------------------------------------------");
#endif

    if (nRank != CREATURE_RANK_INVALID)
    {
#ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","AutoScaling: " + ToString(oCreature) + " to level:" + ToString(nLevel) + " Rank:" + ToString(nRank) + " Class:" + ToString(nClass));
#endif
    }
    else
    {
#ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","AutoScaling to default on " + ToString(oCreature) + ": no rank assigned!");
#endif
        //return;
        if (IsFollower(oCreature) && nRank != 100)
        {
            nRank = CREATURE_RANK_PLAYER;
        }
        else
        {
            nRank = CREATURE_RANK_CRITTER;
        }
    }
#ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "-------------------------------------------------------------");
#endif

    Chargen_InitializeCharacter(oCreature, FALSE); // we don't wipe the heroes ability list
    Chargen_ApplyRaceModifiers(oCreature);

    // -------------------------------------------------------------------------
    // Decide for an auto scaling strategy for this creature
    // -------------------------------------------------------------------------
    int nStrategy = AS_SelectStrategy(oCreature,nClass, nForceClass !=0);

    float fRankModifier     =       GetAutoScaleDataFloat(nRank, AS_RANK_SCALE_FACTOR);
    int   nRankBonusPoints  = GetM2DAInt(Diff_GetAutoScaleTable(),"nBonusPoints",nRank);
    float fRulesPoints = GetCreatureProperty(oCreature, PROPERTY_SIMPLE_ATTRIBUTE_POINTS);


    float fEffectivePoints = (fRankModifier * fTotalPoints) + fRulesPoints;
    float fPointPerNLevels;
    float fWeight;
    float fValue;
    float fSum;
    int i;
    for (i = 1; i <= 6; i++)
    {

        fPointPerNLevels  = GetM2DAFloat(TABLE_AUTOSCALE_DATA, "s" + ToString(i), nStrategy);
#ifdef DEBUG
        Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale",ToString(fPointPerNLevels));
#endif
        if (fPointPerNLevels > 0.0)
        {
            fWeight          = (1/fPointPerNLevels) / AS_POINTS_PER_LEVEL;
            fValue           = ((nLevel-1) * fEffectivePoints * fWeight + (fWeight * nRankBonusPoints) + (fWeight *  fRulesPoints) )+  CHARGEN_BASE_ATTRIBUTE_VALUE;
#ifdef DEBUG
            Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","stat: " + ToString(i) + ": " + ToString(fValue));
#endif
            fSum += (fValue-CHARGEN_BASE_ATTRIBUTE_VALUE);
            if(i == 6 && GetCreatureRank(oCreature) == CREATURE_RANK_ONE_HIT_KILL)
                SetCreatureProperty(oCreature, 6, 10.0, PROPERTY_VALUE_BASE); // minimum const
            else
                SetCreatureProperty(oCreature,i ,  IntToFloat(FloatToInt(fValue + 0.5f)) /*makeint*/, PROPERTY_VALUE_BASE);
        }
        else
        {
#ifdef DEBUG
            Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","error retrieving data for stat : " + ToString(i));
#endif
        }
    }


    AS_SetCommonStats(oCreature);

    AS_AddClassLevels(oCreature, nClass, nLevel, fRankModifier);

    int nPackage = GetPackage(oCreature);
    float fLevelsPerAbility = GetM2DAFloat(TABLE_PACKAGES,"LevelsPerAbility",nPackage);
#ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Package levels per ability:" + ToString(fLevelsPerAbility));
#endif
    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","Core class:" + ToString(GetCreatureCoreClass(oCreature)));
    #endif
    if(fLevelsPerAbility == -1.0) // use class rules instead of package
        fLevelsPerAbility = IntToFloat(GetM2DAInt(TABLE_RULES_CLASSES,"LevelsPerAbility",nClass));
    if(fLevelsPerAbility <= 0.0)
        fLevelsPerAbility = 1.0;
    int nAbilitiesToGive = FloatToInt(IntToFloat(nLevel) / fLevelsPerAbility) + 1 ; // +1 rounding up
    if(fLevelsPerAbility >= 1.0)
        nAbilitiesToGive += 2; // +2 bonus at level 1
    if(GetCreatureRank(oCreature) == CREATURE_RANK_PLAYER && GetCreatureCoreClass(oCreature) != CLASS_ROGUE) // rogues get dirty fighting automatically
        nAbilitiesToGive ++;

    if(nPackage == 91) // mouse
        nAbilitiesToGive = 2;

    //if (GetCreatureRank(oCreature) != CREATURE_RANK_PLAYER)
    AS_AssignAbilities(oCreature, nAbilitiesToGive);

    if(GetCreatureRank(oCreature) == CREATURE_RANK_PLAYER)
        Chargen_SetNumTactics(oCreature);


    SetCreatureProperty(oCreature,PROPERTY_ATTRIBUTE_MELEE_CRIT_MODIFIER,   3.0f * fRankModifier, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oCreature,PROPERTY_ATTRIBUTE_RANGED_CRIT_MODIFIER,  3.0f * fRankModifier, PROPERTY_VALUE_BASE);

#ifdef DEBUG
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","rank:" + ToString(nRank));
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","level:" + ToString(nLevel) + " GetLevel says: " + ToString(GetLevel(oCreature)));
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","total pts:" + ToString(fSum));
    Log_Trace(LOG_CHANNEL_AUTOBALANCE, "sys_autoscale","health:" + ToString(GetMaxHealth(oCreature)));
#endif
    HealCreature(oCreature);

    // Non followers:ensure they can't levelup
    if(GetFollowerState(oCreature) == FOLLOWER_STATE_INVALID)
    {
        SetCreatureProperty(oCreature,PROPERTY_SIMPLE_TALENT_POINTS, 0.0f);
        SetCreatureProperty(oCreature,PROPERTY_SIMPLE_SKILL_POINTS, 0.0f);
        SetCreatureProperty(oCreature,PROPERTY_SIMPLE_ATTRIBUTE_POINTS, 0.0f);
    }

}


void AS_InitCreature(object oCreature, int nLevel = -1, int bSetAbilities = TRUE, int nForceClass=0)
{

#ifdef DEBUG
    Log_Trace (LOG_CHANNEL_AUTOBALANCE,"AS_InitCreature() " + ToString(oCreature) + " " + ToString(nLevel), "");
#endif


    // -------------------------------------------------------------------------
    // Non combatant
    // -------------------------------------------------------------------------
    if (GetCombatantType(oCreature) == CREATURE_TYPE_NON_COMBATANT)
    {
        #ifdef DEBUG
        Log_Trace (LOG_CHANNEL_AUTOBALANCE,"Not scaling " + ToString(oCreature), ": creature is a non combatant!");
        #endif
        return;
    }

    if (IsOneShotKillCreature(oCreature))
    {
           #ifdef DEBUG
           Log_Trace (LOG_CHANNEL_AUTOBALANCE,"Scaling as ONESHOT KILL Creature " + ToString(oCreature), "");
           #endif

           Chargen_ModifyCreaturePropertyBase(oCreature, PROPERTY_SIMPLE_LEVEL,1.0);
           Chargen_ModifyCreaturePropertyBase(oCreature, PROPERTY_SIMPLE_EXPERIENCE,1.0);
           AS_SetDepletable(oCreature,1.0f, PROPERTY_DEPLETABLE_HEALTH);
           return;
    }


    if (GetCreatureProperty(oCreature,PROPERTY_SIMPLE_EXPERIENCE) < 1.0f)
    {
        // -------------------------------------------------------------------------
        // Only creatures alive can be scaled.
        // -------------------------------------------------------------------------
        if(!IsDead(oCreature))
        {
            AS_CommenceAutoScaling(oCreature, nLevel,nForceClass);
        }

    }
    else
    {
        #ifdef DEBUG
        Log_Trace (LOG_CHANNEL_AUTOBALANCE,"Not scaling " + ToString(oCreature), ": already has at least 1 xp!");
        #endif
    }

    Chargen_SetNumTactics(oCreature);
    DEBUG_PrintToScreen("Class:" + ToString(GetCreatureCoreClass(oCreature)));

}



void Levelup_SetReadyToLevelUp(object oPartyMember, int bShowLevelupVFX = TRUE);
void Levelup_SetReadyToLevelUp(object oPartyMember, int bShowLevelupVFX = TRUE)
{
    SetCanLevelUp(oPartyMember,1);


    int nNewLevel = GetLevel(oPartyMember) +1;

    int nXPCur    = GetExperience(oPartyMember);
    int nXPNext   =  GetM2DAInt(TABLE_EXPERIENCE,"XP", nNewLevel);

    int nClass =  GetCreatureCoreClass(oPartyMember);
    int nTalent = GetM2DAInt(TABLE_RULES_CLASSES,"LevelsPerAbility",nClass);
    int nSkill = GetM2DAInt(TABLE_RULES_CLASSES,"LevelsPerSkill",nClass);

    while (nXPCur >= nXPNext && nXPNext > 0)
    {

        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_REWARDS,"Levelup_SetReadyToLevelUp",ToString(nXPCur) + " >= " + ToString(nXPNext)+ "... running levelup",oPartyMember);
        #endif

         if (nTalent> 0 && nNewLevel %nTalent ==0)
        {
            Chargen_ModifyCreaturePropertyBase(oPartyMember,PROPERTY_SIMPLE_TALENT_POINTS, 1.0f);
        }

        if (IsHumanoid(oPartyMember))
        {
            if (nSkill>0 && nNewLevel % nSkill == 0)
            {
                 Chargen_ModifyCreaturePropertyBase(oPartyMember,PROPERTY_SIMPLE_SKILL_POINTS, 1.0f);
            }
        }
        else
        {
            // -----------------------------------------------------------------
            // No XP for non humanoids
            // -----------------------------------------------------------------
            SetCreatureProperty(oPartyMember,PROPERTY_SIMPLE_SKILL_POINTS, 0.0f);
        }

        // Add 3 attribute points
        Chargen_ModifyCreaturePropertyBase(oPartyMember,PROPERTY_SIMPLE_ATTRIBUTE_POINTS, 3.0f);
        AS_AddClassLevels(oPartyMember, nClass,1, 0.0f, FALSE, FALSE);

    `   // ---------------------------------------------------------------------
        // Only humanoids gain specializations
    `   // ---------------------------------------------------------------------
        if (IsHumanoid(oPartyMember))
        {
            float fPoints = GetCreatureProperty(oPartyMember, 38);

            // at 7 as a player and 14, give spec points.
            int nSpec = GetM2DAInt(TABLE_EXPERIENCE, "SpecPoint", nNewLevel);
            if (((nSpec == 1) && (fPoints == 0.0f) && (IsHero(oPartyMember) == TRUE)) || ((nSpec == 2) && (fPoints < 2.0f)) || ((nSpec == 3) && (fPoints < 3.0f)))
            {
                fPoints += 1.0f;
                SetCreatureProperty(oPartyMember, 38, fPoints);
            }
        }

        nXPNext   =  GetM2DAInt(TABLE_EXPERIENCE,"XP", ++nNewLevel);
    }

    // -------------------------------------------------------------------------
    // We have 4, sequentially ordered vfx in vfx_base.
    // depending on new level, different ones play.
    // -------------------------------------------------------------------------

    // Don't apply VFX if you are in cutscenes or a weird mode.
    if(GetGameMode() == GM_COMBAT ||  GetGameMode() == GM_EXPLORE)
    {
            int nVfx =30020;
            if (nNewLevel >5)
                nVfx++;
            if (nNewLevel >10)
            nVfx++;
            if (nNewLevel >15)
                nVfx++;
            if(bShowLevelupVFX)
                ApplyEffectVisualEffect(oPartyMember, oPartyMember, nVfx,EFFECT_DURATION_TYPE_INSTANT,0.0f,0);
    }
    // --> Telemetry stub. New Level is nNewLevel here. <--

}